#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stropts.h>
#include <unistd.h>
#include <math.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include "paint.h"

#define FILENAME "/dev/fb0"

int fb_width;
int fb_height;
int fb_depth;
int fb_bpp;

unsigned char *fbp;

static int fb_size;
static int fd;

static unsigned int color;

void open_fb()
{
	struct fb_var_screeninfo fb_var;
	struct fb_fix_screeninfo fb_fix;

	if ((fd = open(FILENAME, O_RDWR)) == -1)
	{
		fprintf(stderr, "%s: cannot open %s\n", __func__, FILENAME);
		return;
	}

	if (ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix) == -1)
	{
		fprintf(stderr, "%s: error FBIOGET_FSCREENINFO\n", __func__);
		return;
	}

	if (ioctl(fd, FBIOGET_VSCREENINFO, &fb_var) == -1)
	{
		fprintf(stderr, "%s: error FBIOGET_VSCREENINFO\n", __func__);
		return;
	}

	fb_width = fb_var.xres;
	fb_height = fb_var.yres;
	fb_depth = fb_var.bits_per_pixel;

	fb_bpp = fb_depth / 8;
	fb_size = fb_height * fb_width * fb_bpp;

	if ((fbp = (unsigned char *) mmap(NULL, fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED)
	{
		fprintf(stderr, "%s: mmap failed\n", __func__);
		return;
	}
}

void close_fb()
{
	if (munmap(fbp, fb_size) == -1)
	{
		fprintf(stderr, "%s: munmap failed\n", __func__);
		return;
	}
	fbp = NULL;

	close(fd);
}

unsigned long rgb(unsigned char r, unsigned char g, unsigned char b)
{
	if (fb_depth == 16)
		return ((r >> 3) << 11) + ((g >> 2) << 5) + (b >> 3);
	else
		return (r << 16) + (g << 8) + b;
}

void set_color(unsigned int col)
{
	color = col;
}

void pixel(int x, int y)
{
	if (x < 0 || x > fb_width - 1 || y < 0 || y > fb_height - 1)
		return;

	memcpy(fbp + (y * fb_width + x) * fb_bpp, &color, fb_bpp);
}

void line(int x1, int y1, int x2, int y2)
{
	int t;
	double k;

	if (y1 == y2)
	{
		if (x1 > x2)
		{
			t = x1;
			x1 = x2;
			x2 = t;
		}

		for (; x1 <= x2; ++x1)
			pixel(x1, y1);

	}
	else if (x1 == x2)
	{
		if (y1 > y2)
		{
			t = y1;
			y1 = y2;
			y2 = t;
		}

		for (; y1 <= y2; ++y1)
			pixel(x1, y1);
	}
	else
	{
		if (x1 > x2)
		{
			t = x1;
			x1 = x2;
			x2 = t;
			t = y1;
			y1 = y2;
			y2 = t;
		}

		k = (double) (y2 - y1) / (double) (x2 - x1);
		for (; x1 <= x2; ++x1)
			pixel(x1, (int) ((x1 - x2) * k + y2));
	}
}

void rectangle(int x1, int y1, int x2, int y2)
{
	for (; y1 <= y2; ++y1)
		line(x1, y1, x2, y1);
}

void circle(int x, int y, int r)
{
	int i, j;

	for (i = 0; i < 2 * r + 1; ++i)
		for (j = 0; j < 2 * r + 1; ++j)
			if (((i - r) * (i - r) + (j - r) * (j - r)) <= r * r + r)
				pixel(x - r + i, y - r + j);
}
